package com.lm.controller;

import com.lm.domain.Person;
import com.lm.domain.Product;
import com.lm.domain.User;
import com.lm.service.UserService;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.http.*;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.context.request.async.WebAsyncTask;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.springframework.web.util.UriComponents;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


/**
 * @author liming
 * @create 2017-01-17-20:20
 *
 */

@Controller
public class HomeController {

    private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

    @RequestMapping(value = "index", method = RequestMethod.GET)
    public  String index() {
        logger.info("home页面");
        return "home";
    }

    @Autowired
    private UserService userService;

    /**
     * 增
     * @param user
     * @param modelMap
     * @return
     */
    @RequestMapping(path = "user", method = RequestMethod.POST)
    public String processSubmit(@ModelAttribute User user, ModelMap modelMap) {
        logger.info("表单：" + user);
        if (userService.insert(user) == 1) {
            modelMap.addAttribute("msg", "注册成功！");
        } else {
            modelMap.addAttribute("msg", "注册失败！");
        }
        return "msg";
    }

    /**
     * 查全部
     * 使用@ResponseBody注解映射响应体
     * 应用于方法上，标志该方法的返回值将直接写回到HTTP响应体中去,
     * 而不会被被放置到Model中或被解释为一个视图名
     * text/html;charset=UTF-8   application/json; charset=utf-8
     * @return
     */
    @RequestMapping(path = "all-user", method = RequestMethod.GET)
    @ResponseBody
    public String queryAllUser() {
        return userService.getAll().toString();
    }
    /**
     * 矩阵变量  /matrixVars/42;q=11;r=12/pets/21;q=22;s=23/t.action?number=1
     * @param matrixVars
     * @param petMatrixVars
     */
    @RequestMapping(path = "matrixVars/{ownerId}/pets/{petId}", method = RequestMethod.GET)
    public void findPet(@MatrixVariable Map<String, String> matrixVars, @MatrixVariable(pathVar="petId") Map<String, String> petMatrixVars) {

        // matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
        // petMatrixVars: ["q" : 11, "s" : 23]
        System.out.println("matrixVars:" + matrixVars + "petMatrixVars:" + petMatrixVars);
    }

    @RequestMapping(path = "process", method = RequestMethod.GET)
    public String processSubmit(@ModelAttribute("pet") String pet, BindingResult result, Model model) {
        System.out.println("processSubmit" + pet + result.toString());
        return pet;
    }


    /**
     * 使用@RequestBody注解映射请求体,暗示了方法参数应该被绑定了HTTP请求体的值
     * @param body
     * @param writer
     * @throws IOException
     */
    @RequestMapping(path = "RequestBody", method = RequestMethod.GET)
    public void handle(@RequestBody String body, Writer writer) throws IOException {
        writer.write(body);
    }

    /**
     * 使用@ResponseBody注解映射响应体
     * 应用于方法上，标志该方法的返回值应该被直接写回到HTTP响应体中去,
     * 而不会被被放置到Model中或被解释为一个视图名
     * text/html;charset=UTF-8   application/json; charset=utf-8
     * @return
     */
    @RequestMapping(path = "ResponseBody", method = RequestMethod.GET, produces = "text/html;charset=UTF-8")
    @ResponseBody
    public String helloWorld() throws Exception {
        if (true){
            throw new Exception("hello world exception test测试");
        }
        return "Hello World";
    }


    /**
     * 文件上传  MultipartFile
     * @param name
     * @param file
     * @param modelMap
     * @return
     * @throws IOException
     */
    @RequestMapping(path = "fileupload", method = RequestMethod.POST)
    public String handleFileUpload(@RequestParam("name") String name, @RequestParam("file") MultipartFile file, ModelMap modelMap) throws IOException {

        if (!file.isEmpty()) {
            byte[] bytes = file.getBytes();
            logger.info("文件名:" + name + "  文件大小:" + bytes.length + " 字节");
            // store the bytes somewhere
            modelMap.addAttribute("msg", "上传成功！");
        } else {
            modelMap.addAttribute("msg", "上传失败！");
        }
        return "msg";
    }

    /**
     * IO异常处理
     * 在控制器内部定义，会接收并处理控制器（或其任何子类）中的@RequestMapping方法抛出的异常。
     * 如果@ExceptionHandler方法定义在@ControllerAdvice类中，会处理相关控制器中抛出的异常。
     * 如果没有给注解任何参数值，默认处理的异常类型是方法参数所声明的异常。
     * @param ex
     * @return
     */
    @ExceptionHandler(IOException.class)
    public ResponseEntity<String> handleIOException(IOException ex) {
        HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.set("MyResponseHeader", "MyValue");
        return new ResponseEntity<String>("异常处理器:  异常信息：" + ex.getMessage(), responseHeaders, HttpStatus.CREATED);
    }
    @RequestMapping(value="IOException", method = RequestMethod.GET)
    public void ioException() throws Exception{
        throw new IOException("IO异常测试");
    }

    /**
     * 用@RequestParam将请求参数绑定至方法参数
     * 类型级别的@SessionAttributes注解声明了某个特定处理器所使用的会话属性
     * 它会列出该类型希望存储到session或converstaion中的model属性名或model的类型名，
     * 一般是用于在请求之间保存一些表单数据的bean。
     */
    @Controller
    @SessionAttributes(value = {"user", "session"})
    public class Session {
        @RequestMapping(value = "session")
        public String session(ModelMap modelMap) {
            modelMap.addAttribute("session", "session测试");
            return "msg";
        }

        /**
         * 方法通过返回值的方式默认添加一个属性
         * @return
         */
        @ModelAttribute("user")
        public User getUser() {
            return new User(1, "张三", "1234", 12);
        }
    }

    /**
     * ResponseEntity可以存取请求头和响应头,对象体将被HttpMessageConverters转换成响应流  , produces = "text/html; charset=UTF-8"
     * @param id 路径变量
     * @return
     */
    @RequestMapping(path = "pathVariable/{id}")
    public ResponseEntity<String> showBook(@PathVariable Long id) {
        return ResponseEntity
                .ok()
                //Cache-Control与Expires的作用一致，都是指明当前资源的有效期，
                //控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据
                .cacheControl(CacheControl.maxAge(3, TimeUnit.SECONDS))
                // 这里也能操作最后修改时间lastModified
                .lastModified(System.currentTimeMillis())
                .eTag("v1.0")
                .body("实例 id = " + id);
    }



    /**
     * 注解@CookieValue将一个方法参数与一个HTTP cookie的值进行绑定。
     * 这个注解可以注解到处理器方法上
     * @param cookie
     */
    @RequestMapping("cookie")
    public String displayHeaderInfo(@CookieValue("JSESSIONID") String cookie, ModelMap model) {
        model.addAttribute("msg", "JSESSIONID:" + cookie);
        //logger.info("JSESSIONID:" + cookie);
        return "msg";
    }

    /**
     * 注解@RequestHeader能将一个方法参数与一个请求头属性进行绑定
     * 注解可以注解到处理器方法上
     * @param encoding
     * @param connection
     */
    @RequestMapping("head")
    public String displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding, @RequestHeader("Connection") String connection, ModelMap model) {
        //logger.info("Accept-Encoding:" + encoding + "  Keep-Alive:" + connection);
        model.addAttribute("msg", "Accept-Encoding:" + encoding + "  Connection:" + connection);
        return "msg";
    }

    /**
     * 在控制器内使用@InitBinder注解的方法
     * 或在注解了@ControllerAdvice的类中使用@InitBinder注解的方法
     * 注解@InitBinder方法也可以定义在@ControllerAdvice注解的类上
     *
     * 操作所有java.util.Date类型的表单字段
     * @param binder
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    }
    @InitBinder
    public void initBinder1(WebDataBinder binder) {
        binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
    }
    /**
     * 数据绑定测试 bind/?date=2018-1-29
     * 参数名为方法参数名
     * @param date
     * @param modelMap
     * @return
     */
    @RequestMapping(path = "bind", method = RequestMethod.GET)
    public String date(Date date, ModelMap modelMap){
        modelMap.addAttribute("msg", "数据绑定测试  日期:" + date + "   时间戳：" + date.getTime());
        return "msg";
    }

    /**
     * redirect测试
     * @param attributes
     * @return
     */
    @RequestMapping(path = "redirect")
    public String redirect(RedirectAttributes attributes){
        attributes.addFlashAttribute("msg", "redirect测试");
        return "redirect:msg";
    }
    /**
     * 使用@ModelAttribute注解获取之前addFlashAttribute添加的数据
     * @param msg
     * @param modelMap
     * @return
     */
    @RequestMapping(path = "msg")
    public String msg(@ModelAttribute("msg") String msg, ModelMap modelMap){
        System.out.println("msg:" + msg);
        modelMap.addAttribute("msg", msg);
        return "msg";
    }


    public static class Data{
        private Date date;

        public Date getDate() {
            return date;
        }

        public void setDate(Date date) {
            this.date = date;
        }
    }
    /**
     * 方法参数上的@ModelAttribute注解说明参数应该从模型（Model）中获取
     * 根据对向内部变量名自动填充Data对象
     * 如果model中找不到，那么该参数会先被实例化，然后被添加到model中。
     * 在model中存在以后，请求中所有名称匹配的参数都会填充到该参数中。
     * @param data
     * @param result
     * @param modelMap
     * @return
     */
    @RequestMapping(path = "bindModelAttribute", method = RequestMethod.GET)
    public String date(@ModelAttribute("data") Data data, BindingResult result, ModelMap modelMap){
        if (result.hasErrors()) {
            modelMap.addAttribute("msg", result.toString());
            logger.info("BindingResult:" + result.toString());
            return "msg";
        } else {
            modelMap.addAttribute("msg", "数据绑定测试  日期:" + data.getDate() + "时间戳：" + data.getDate().getTime());
        }
        return "msg";
    }
    /**
     * 在RequestMapping之前被访问
     * 方法通过返回值的方式默认地将添加一个属性，不能直接被映射到具体的请求上。
     * 在同一个控制器中，注解了@ModelAttribute的方法实际上会在@RequestMapping方法之前被调用
     * 方法接收一个Model对象，然后可以向其中添加任意数量的属性
     * @param num
     * @return
     */
    @ModelAttribute
    public String addAccount(@RequestParam(required = false) String num) {
        return num;
    }
//    /**
//     * 方法接收一个Model对象，然后可以向其中添加任意数量的属性
//     * @param num
//     * @param model
//     */
//    @ModelAttribute
//    public void populateModel(@RequestParam(required = false) String num, @RequestParam(required = false) Model model) {
//        model.addAttribute(num);
//        // add more ...
//    }


    /**
     * 异步请求处理
     * 当Callable抛出异常时，Spring MVC会把一个Exception对象分派给Servlet容器进行处理，
     * 而不是正常返回方法的返回值，然后容器恢复对此异步请求异常的处理。
     * 若方法返回的是一个DeferredResult对象，可以选择调Exception实例的setResult方法,setErrorResult方法。
     * @return
     *
     * 使用@ResponseBody注解可以返回String
     */
    @RequestMapping(path = "async", method = RequestMethod.GET)
    public Callable<ModelAndView> asyncProcess(final ModelMap modelMap) {

        return new Callable<ModelAndView>() {
            @Override
            public ModelAndView call() throws Exception {
                ModelAndView mav = new ModelAndView("msg");
                mav.addObject("msg", "执行成功");
                //mav.getModelMap().addAttribute("msg", "执行成功 ModelMap");
                //modelMap.addAttribute("msg", "延时1秒");
                //mav.addObject("msg", modelMap);
                return mav;
            }
        };
    }

    @RequestMapping(value="longTimeTask", method = RequestMethod.GET)
    public WebAsyncTask longTimeTask(){
        logger.info("longTimeTask 被调用 thread id is : " + Thread.currentThread().getId());
        Callable<ModelAndView> callable = new Callable<ModelAndView>() {
            @Override
            public ModelAndView call() throws Exception {
                //假设是一些长时间任务
                Thread.sleep(3000);
                ModelAndView mav = new ModelAndView("msg");
                mav.addObject("msg", "执行成功");
                logger.info("执行成功 thread id is : " + Thread.currentThread().getId());
                return mav;
            }
        };
        return new WebAsyncTask(callable);
    }

    @RequestMapping(value="outtime", method = RequestMethod.GET)
    public WebAsyncTask outTimeTask(){
        logger.info("outTimeTask被调用 thread id is : " + Thread.currentThread().getId());
        Callable<ModelAndView> callable = new Callable<ModelAndView>() {
            @Override
            public ModelAndView call() throws Exception {
                Thread.sleep(4000);
                ModelAndView mav = new ModelAndView("msg");
                mav.addObject("msg", "执行成功");
                logger.info("执行成功 thread id is : " + Thread.currentThread().getId());
                return mav;
            }
        };
        WebAsyncTask asyncTask = new WebAsyncTask(2000, callable);
        asyncTask.onTimeout(new Callable<ModelAndView>() {
            @Override
            public ModelAndView call() throws Exception {
                ModelAndView mav = new ModelAndView("msg");
                mav.addObject("msg", "执行超时");
                logger.info("执行超时 thread id is ：" + Thread.currentThread().getId());
                return mav;
            }
        });
        return asyncTask;
    }

    @RequestMapping(path = "url")
    public void url(HttpServletRequest request, HttpServletResponse response) throws IOException {
        UriComponents ucb = ServletUriComponentsBuilder.fromRequest(request)
                                    .replaceQueryParam("accountId", "{id}").build()
                                    .expand("123")
                                    .encode();
        UriComponents ucb1 = ServletUriComponentsBuilder.fromContextPath(request).path("/accounts").build();
        logger.info(ucb.toUri() + "   " + ucb1.toUri());
                                                                                                    //controller中的方法名    路径变量
        UriComponents uriComponents = MvcUriComponentsBuilder.fromMethodName(HomeController.class, "showBook", 521L)
                //参数填充
                .replaceQueryParam("date", "2018-1-29")
                .buildAndExpand(42);
        URI uri = uriComponents.encode().toUri();
        logger.info("uri:" + uri);
        response.sendRedirect(uri.toString());
    }

    /**
     * 向客户端推送事件
     * ResponseBodyEmitter也可以被放到ResponseEntity体里面使用
     * @return
     */
    @RequestMapping("events")
    public ResponseBodyEmitter handle() {
        ResponseBodyEmitter emitter = new ResponseBodyEmitter();
        try {
            emitter.send("Hello once\n");
            emitter.send("Hello again");
            emitter.complete();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return emitter;
    }

    @RequestMapping("event")
    public SseEmitter push() {
        SseEmitter emitter = new SseEmitter();
        try {
            emitter.send("Hello World");
            emitter.send("Hello again");
            emitter.complete();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return emitter;
    }

    /**
     * 直接写回输出流OutputStream的HTTP Streaming
     * ResponseBodyEmitter也可以被放到ResponseEntity体里面使用
     * @return
     */
    @RequestMapping("download")
    public StreamingResponseBody download() {
        return new StreamingResponseBody() {
            @Override
            public void writeTo(OutputStream outputStream) throws IOException {
                //outputStream.write(new byte[]{73, 76, 89});
                //outputStream.write(new byte[]{0x1, 0x2, 0x3});
                File file = new File("D:\\0.jpg");
                outputStream.write(FileUtils.readFileToByteArray(file));
                outputStream.flush();
            }
        };
    }

    /**
     * springmvc中支持的下载
     */
    @RequestMapping("/downfile")
    public ResponseEntity<byte[]> downloadFile() throws IOException {
        File file = new File("D:\\log.txt");
        //处理显示中文文件名的问题
        String fileName = new String(file.getName().getBytes("utf-8"),"ISO-8859-1");
        //设置请求头内容,告诉浏览器代开下载窗口
        HttpHeaders headers = new HttpHeaders();
        headers.setContentDispositionFormData("attachment", fileName);
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        return new ResponseEntity<>(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED);
    }

    /**
     * 资源获取的方式
     */
    @Value("#{path['filePath']}")
    private String filePath;

    @Value("#{path['processPath']}")
    private String processPath;

    @RequestMapping(path = "files", method = RequestMethod.GET)
    public String getFiles(ModelMap map){
        map.addAttribute("files", getFile(filePath));
        map.addAttribute("msg", "文件列表");
        return "msg";
    }
    @RequestMapping(path = "open", method = RequestMethod.GET)
    public String open(@RequestParam String name, ModelMap map) throws IOException {
        map.addAttribute("files", getFile(filePath));
        map.addAttribute("msg", "文件列表");
        Runtime runtime=Runtime.getRuntime();
        String r = java.net.URLDecoder.decode(name, "UTF-8");
        logger.info(name + " " + r);
        String[] commandArgs={processPath, filePath + r};
        runtime.exec(commandArgs);
        return "msg";
    }
    public List<String> getFile(String path) {
        File file = new File(path);
        File[] array = file.listFiles();
        List<String> list = new ArrayList<>();
        for (int i = 0; i < array.length; i++) {
            if (array[i].isFile()) {
                list.add(array[i].getName());
            }
        }
        return list;
    }
    /**
     * freemarker 测试
     * 参数可以使用HttpServletRequest request
     * @param root
     * @return
     */
    @RequestMapping(path = "free", method = RequestMethod.GET)
    public String freeMaker(ModelMap root) {
        //定义数据
        //Map<String,Object> root = new HashMap<>();

        //Map<Person>
        Person person = new Person();
        person.setId("111");
        person.setName("张三");
        root.put("person", person);
        root.put("world", "Hello World");
        root.put("worlds", new String[]{"Hello", "World", "test"});

        //Map<List>
        List<String> persons = new ArrayList<>();
        persons.add("阿灵罗");
        persons.add("罗零");
        persons.add("灵罗");
        root.put("persons",persons);

        //时间格式
        root.put("cur_time", new Date());

        //Map<Map>
        Map<String,String> mx = new HashMap<>();
        mx.put("alingluo", "阿灵罗");
        mx.put("lling", "罗零");
        root.put("mx", mx);

        //遍历 Map<List<Map>>
        List<Map<String,String>> list = new ArrayList<Map<String,String>>();
        Map<String,String> map1 = new HashMap<String,String>();
        map1.put("id1", "张三");
        map1.put("id2", "李四");
        Map<String,String> map2 = new HashMap<String,String>();
        map2.put("id1", "张三");
        map2.put("id2", "李四");
        list.add(map1);
        list.add(map2);
        root.put("list", list);
        //定义数据
        root.put("val", null);
        return "freemarker";
    }

    /**
     * ajax 测试
     * @return
     */
    @RequestMapping(value="in", method = RequestMethod.GET)
    public String view() {
        return "ajax";
    }
    /**
     * 资源获取的方式
     */
    @Resource(name = "config")
    private Product product;

}
